#include <linux/irq.h>
#include <linux/i2c.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/pinctrl/consumer.h>
#include <linux/input-event-codes.h>
#include <linux/input.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/input/mt.h>
#include <linux/of_irq.h>


// 版本号寄存器地址
#define GT9157_VERSION_REG     0x8140  
#define READ_COOR_ADDR         0x814e
#define MAX_TOUCH_POINT_NUM 3

#define GTP_DEBUG(fmt,arg...)    do{\
    printk("<<luo-gt9157>> [%d]"fmt"\n",__LINE__, ##arg);\
  }while(0)

extern struct input_dev *devm_input_allocate_device(struct device *dev);
extern int input_register_device(struct input_dev *dev);

struct my_own_gt9xx_touchscreen_driver
{
    struct i2c_client *client;
    struct input_dev * input_dev;
    int touch_irq;
};



irqreturn_t screen_touched_interrupt_handler(int irq, void * data_in){

   
    /**
     * Bit7: Buffer status, 1 = coordinate (or key) is ready for host to read; 0 = coordinate (or key) is not ready and
     *   data is not valid. After reading coordinates, host should configure this flag (or the entire byte) to 0 via I2C.
     */
    bool buffer_status ;

    /**
     * Bit4: HaveKey, 1 : Have touch key; 0 : No touch key (released). 
     */
    bool haveKey;

    unsigned int touch_num,point_x,point_y,point_size,track_id;
    struct my_own_gt9xx_touchscreen_driver *data  = data_in;
    struct i2c_client * client = data->client;
    struct input_dev *input_dev = data->input_dev;

    unsigned char coordinate_reg_addr[3] = {READ_COOR_ADDR>>8,READ_COOR_ADDR&0xff};
    unsigned char buf[1+8*MAX_TOUCH_POINT_NUM];
    
    // 存储触控点（从point开始数8个字节是一个point的所有信息）
    unsigned char * point;

    size_t i;
    // 用于存储上一次触碰了几个点
    static int pretouch_num;

    //disable_irq_nosync(data->touch_irq);
    
    // 先发送要读取的寄存器地址
    // 注意这里数组有三个字节，但是只写前2个字节到触摸屏
    i2c_master_send(client,coordinate_reg_addr,2);
    // 接收数据
    i2c_master_recv(client,buf,sizeof(buf));

    buffer_status = (buf[0]>>7) & 0x01;
    haveKey = (buf[0]>>4) & 0x01;
    touch_num = buf[0] & 0x0f;

    touch_num = touch_num<MAX_TOUCH_POINT_NUM?touch_num:MAX_TOUCH_POINT_NUM;

    if(!buffer_status){
        GTP_DEBUG("coordinate (or key) is not ready and data is not valid");
        return IRQ_HANDLED;
    }
    

    GTP_DEBUG("interrupt received 0x814e = %#x touch_num =%d input_dev = %p",buf[0],touch_num,(void *)input_dev);

    // 设定输入设备的触摸槽位
    // input_mt_slot(input_dev, 0);
    if(touch_num){
        // 按下

        for (point = buf+1,i=0; i <  touch_num ; point+=8,i++)
        {
        
        
            track_id = point[0];

            point_x = (point[2] << 8) |  point[1];
            point_y = (point[4] << 8) |  point[3];
            point_size= (point[6] << 8) |  point[5];
              
            GTP_DEBUG("point %ld touched ID:%d, X:%d, Y:%d, W:%d",i, track_id, point_x, point_y, point_size);
        
            /* 上报触摸点 1 的坐标 */
            input_mt_slot(input_dev, i);  // 选择触摸点 1
            input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, true);  // 触摸点有效
            input_report_abs(input_dev, ABS_MT_POSITION_X, point_x);  // X 坐标
            input_report_abs(input_dev, ABS_MT_POSITION_Y, point_y);  // Y 坐标
            input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, point_size);
            input_report_abs(input_dev, ABS_MT_WIDTH_MAJOR, point_size);
            input_report_abs(input_dev, ABS_MT_TRACKING_ID, track_id);

            
        }
    }else if(pretouch_num){

        /* 上报触摸松开事件 */

        for (point = buf+1, i = 0; i < pretouch_num; i++,point+=8)
        {
            track_id = point[0];
            GTP_DEBUG("button %d released",track_id);
            input_mt_slot(input_dev, track_id);  // 选择触摸点 
            input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false);  // 触摸点无效
            
        }
        
  
    }/*else{
        input_mt_slot(input_dev, 0);  // 选择触摸点 0
        input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false);  // 触摸点无效

    }*/

    // 报告输入设备的指针仿真信息
    input_mt_report_pointer_emulation(input_dev, true);


    input_sync(input_dev);

    pretouch_num = touch_num;
   
    // 写0到寄存器以清除中断
    i2c_master_send(client,coordinate_reg_addr,sizeof(coordinate_reg_addr));
    //enable_irq(data->touch_irq);


    return IRQ_HANDLED;
}

int do_request_input_dev(struct device *dev,struct my_own_gt9xx_touchscreen_driver *data){

    unsigned int max_x;
    unsigned int max_y;
    int err;

    struct device_node *np = dev -> of_node;

    if(of_property_read_u32(np, "max-x", &max_x)){
        dev_err(dev,"read max-x failed.\n");
        return -EINVAL;
    }

    if(of_property_read_u32(np, "max-y", &max_y)){
        dev_err(dev,"read max-y failed.\n");
        return -EINVAL;
    }

    data->input_dev = devm_input_allocate_device(dev);
    if(!data->input_dev){
        dev_err(dev,"failed to allocate input device.\n");
        return -ENOMEM;
    }else{
        GTP_DEBUG("success to allocate input_dev and its address is %p",(void *)data->input_dev);
    }

    /* 设置支持的事件类型 */
    set_bit(EV_ABS, data->input_dev->evbit);  // 支持绝对坐标事件
    set_bit(EV_KEY, data->input_dev->evbit);  // 支持按键事件（可选）

    /* 设置支持的绝对坐标事件 */
    set_bit(ABS_MT_POSITION_X, data->input_dev->absbit);  // 支持 X 坐标
    set_bit(ABS_MT_POSITION_Y, data->input_dev->absbit);  // 支持 Y 坐标
    set_bit(ABS_MT_TOUCH_MAJOR, data->input_dev->absbit);  // 支持触摸面积（可选）

    data->input_dev->name = "luo_gt9157";

    /* 设置坐标范围 */
    input_set_abs_params(data->input_dev, ABS_MT_POSITION_X, 0, max_x, 0, 0);  // X 坐标范围
    input_set_abs_params(data->input_dev, ABS_MT_POSITION_Y, 0, max_y, 0, 0);  // Y 坐标范围
    input_set_abs_params(data->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);  // 触摸面积范围

    // 配置多点触控支持
    // 必须配置：否则触摸之后，无法释放（只能点击，不能释放）
    // 暂时只实现三点触摸
    err = input_mt_init_slots(data->input_dev, MAX_TOUCH_POINT_NUM, 0);
    if(err){
        dev_err(dev,"failed to init multiple slot.\n");
        return err;
    }
    

    if(input_register_device(data->input_dev)){
        dev_err(dev,"failed to register input device.\n");
        return -EINVAL;
    }


    return 0;
}
/**
 * 通过pinctrl子系统，把触摸屏的 interrupt 引脚复用为 gpio模式
 * 把 reset 引脚 复用为gpio模式
 */
int init_pinctrl_irq_reset_to_gpio(struct device *dev){

    struct pinctrl * pinctrl;
    struct pinctrl_state *default_pinctrl_state;
    int result;

    pinctrl = devm_pinctrl_get(dev);
    if(IS_ERR_OR_NULL(pinctrl)){
        dev_err(dev,"failed to get pinctrl.\n");
        return PTR_ERR(pinctrl);
    }

    default_pinctrl_state = pinctrl_lookup_state(pinctrl,"default");

    if(IS_ERR_OR_NULL(default_pinctrl_state)){
        dev_err(dev,"failed to get pinctrl default state.\n");
        return PTR_ERR(default_pinctrl_state);
    }

    result = pinctrl_select_state(pinctrl,default_pinctrl_state);
    if(result){
        dev_err(dev,"failed to select default pinctrl state.\n");
        return result;
    }

    return 0;
}

int test_gt9157(struct i2c_client * client){
    int result;
    char buf[11];
    unsigned short firmware_version,x_coordinate_resolution,y_coordinate_resolution;
    char vendor_id;

    char version_register_address[] = {
        GT9157_VERSION_REG  >> 8, //高8位
        GT9157_VERSION_REG & 0xff        // 低8位
    };

    // 根据gt9157 编程手册中的通信协议，先写入要读取的寄存器地址
    result = i2c_master_send(client,version_register_address,sizeof(version_register_address));
    

    result = i2c_master_recv(client,buf,sizeof(buf));
    

    firmware_version = buf[5] << 8 | buf[4];
    x_coordinate_resolution = buf[7] << 8 | buf[6];
    y_coordinate_resolution = buf[9] << 8 | buf[8]; 
    vendor_id = buf[10];

    // %.4s 代表只打印前四个字符
    GTP_DEBUG("product id = [%.4s] firmware version = %d x_coordinate_resolution = %d y_coordinate_resolution = %d vendor_id = %d", buf,firmware_version,x_coordinate_resolution,y_coordinate_resolution,vendor_id);
  

    return 0;
}

int touch_screen_probe(struct i2c_client * client, const struct i2c_device_id * dev_id){


    int ret;
    struct gpio_desc * touch_irq_pin, *reset_pin;
    int touch_irq;
    struct device *dev = &client->dev;
    struct my_own_gt9xx_touchscreen_driver *data;

    data = devm_kzalloc(dev,sizeof(*data),GFP_KERNEL);
    
    if(data == NULL){
        pr_err("allocate memory failed.\n");
        return -ENOMEM;
    }
    

    ret = do_request_input_dev(dev, data );
    if(ret){
        return ret;
    }
    GTP_DEBUG("after register input_dev, its address is %p",(void *)data->input_dev);

    ret = init_pinctrl_irq_reset_to_gpio(dev);
    if(ret){
        return ret;
    }


    touch_irq_pin = devm_gpiod_get_index(dev,"touch-irq",0, GPIOD_ASIS);
    if(IS_ERR(touch_irq_pin)){
        dev_err(dev,"failed to get touch-irq gpio.\n");
        return PTR_ERR(touch_irq_pin);
    }

    reset_pin = devm_gpiod_get_index(dev, "reset", 0, GPIOD_ASIS);
    if(IS_ERR(reset_pin)){
        dev_err(dev,"failed to get reset gpio.\n");
        return PTR_ERR(reset_pin);
    }


    // 复位触摸屏

    //1、reset 输出低
    ret = gpiod_direction_output(reset_pin,1);
    if(ret){
        dev_err(dev,"failed to output low on reset pin.\n");
        return ret;
    }
    //2、 interrupt 输出低
    ret = gpiod_direction_output(touch_irq_pin,1);
    if(ret){
        dev_err(dev,"failed to output low on touch_irq pin.\n");
        return ret;
    }
    /* 延迟100us
            注意事项：
        udelay 适用于短时间的延迟（通常小于 1000 微秒）。

        延迟的时间是忙等待（busy-wait），即 CPU 会一直占用，直到延迟结束。

        在高精度场景中，udelay 的精度可能受到 CPU 频率和调度的影响。
    */
    udelay(150);
    
    //3、reset 输出高
    ret = gpiod_direction_output(reset_pin,0);
    if(ret){
        dev_err(dev,"failed to output high on reset pin.\n");
        return ret;
    }
    // 延迟10ms （编程手册上要求5ms）
    mdelay(10);
    //4、interrupt 转为浮空输入状态
    ret = gpiod_direction_input(touch_irq_pin);


    touch_irq = gpiod_to_irq(touch_irq_pin);

    data->touch_irq = touch_irq;
    
    data->client = client;

    /*
        等价于 dev->driver_data = data;
    */
    dev_set_drvdata(dev,data);
   
    
    /*
    IRQF_ONESHOT 能自动在中断下半部自动屏蔽中断，执行完成之后自动启用中断
    */
    ret = devm_request_threaded_irq(dev, touch_irq, NULL, 
        screen_touched_interrupt_handler, 
        IRQF_TRIGGER_RISING /*| IRQF_TRIGGER_FALLING*/ | IRQF_ONESHOT , 
            "name", data);
    
    test_gt9157(client);
    
    return 0;
}



int touch_screen_remove(struct i2c_client * client){

    struct my_own_gt9xx_touchscreen_driver *data;

    data = dev_get_drvdata(&client->dev);
    // 同步关闭中断
    disable_irq(data->touch_irq);
    //手动取消注册会使得内核崩溃 input_unregister_device(data->input_dev);
    return 0;
}

static struct of_device_id device_ids[]={
    {
        .compatible="luo,my-touchscreen-driver"
    }
};

static struct i2c_driver goodix_ts_driver = {
    .probe      = touch_screen_probe,
    .remove     = touch_screen_remove,

    .driver = {
        .name     = "my-touchscreen-driver",
	    .of_match_table = of_match_ptr(device_ids),
    },
};

static int goodix_ts_init(void)
{
    int ret;
    ret = i2c_add_driver(&goodix_ts_driver);
    return ret; 
}

static void goodix_ts_exit(void)
{

    i2c_del_driver(&goodix_ts_driver);

}
//late_initcall(goodix_ts_init);
module_init(goodix_ts_init);

module_exit(goodix_ts_exit);

MODULE_DESCRIPTION("GTP Series Driver");
MODULE_LICENSE("GPL");